﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

namespace WrapGenerator {
    /// <summary>
    /// Not used anymore
    /// </summary>
    internal class ProxyGenerator {
        private string prefix = "wg";
        private StringWriter codeOutput = new StringWriter();
        private string connectionString;
        private ArrayList typesGenerated = new ArrayList();
        private ArrayList gettersGenerated = new ArrayList();

        public object GenerateProxy(Type t) {
            typesGenerated.Clear();
            generateClassProxy(t);
            CompilerParameters cp = new CompilerParameters(new string[] { "System.dll", "System.Data.dll", "System.Xml.dll", t.Module.Name });
            CompilerResults cr = new CSharpCodeProvider().CompileAssemblyFromSource(cp, codeOutput.ToString());
            return cr.CompiledAssembly.CreateInstance(t.Name + "Proxy");
        }

        private void generateClassProxy(Type t) {
            codeOutput.WriteLine("using System;");
            codeOutput.WriteLine("using System.Data;");
            codeOutput.WriteLine("using System.Data.SqlClient;");
            codeOutput.WriteLine("using {0};", t.Namespace);
            codeOutput.WriteLine("");
            codeOutput.WriteLine("public class " + t.Name + "Proxy {");
            codeOutput.WriteLine("    ");
            codeOutput.WriteLine("    public " + t.Name + "Proxy () {");
            codeOutput.WriteLine("        ");
            codeOutput.WriteLine("    }");
            codeOutput.WriteLine(@"    private void GeneralInsert(string table, string[] fieldNames, params object[] values) {
        
			string query = ""INSERT INTO "" + table + ""("";
            bool first = true;
			foreach (string field in fieldNames) {
				if (first) {
					query += field;
					first = false;
				} else {
					query += "", "" + field;
				}
			}
			query += "") VALUES("";
			first = true;
			for (int i = 0; i < values.Length; i++) {
				if (first) {
					query += ""@p"" + i.ToString();
					first = false;
				} else {
					query += "", @p"" + i.ToString();
				}
			}
			query += "")"";
            SqlConnection dbConnection = new SqlConnection(""" + connectionString + @""");
            dbConnection.Open();
			SqlCommand comm = new SqlCommand(query, dbConnection);
			for (int i = 0; i < values.Length; i++) {
				comm.Parameters.Add(""@p"" + i.ToString(), values[i]);
			}
			comm.ExecuteNonQuery();
		
	}");
            codeOutput.WriteLine(@"    private int GeneralInsertIdentity(string table, string[] fieldNames, params object[] values) {
        
			string query = ""INSERT INTO "" + table + ""("";
            bool first = true;
			foreach (string field in fieldNames) {
				if (first) {
					query += field;
					first = false;
				} else {
					query += "", "" + field;
				}
			}
			query += "") VALUES("";
			first = true;
			for (int i = 0; i < values.Length; i++) {
				if (first) {
					query += ""@p"" + i.ToString();
					first = false;
				} else {
					query += "", @p"" + i.ToString();
				}
			}
			query += ""); SELECT @@IDENTITY FROM "" + table;
            SqlConnection dbConnection = new SqlConnection(""" + connectionString + @""");
            dbConnection.Open();
			SqlCommand comm = new SqlCommand(query, dbConnection);
			for (int i = 0; i < values.Length; i++) {
				comm.Parameters.Add(""@p"" + i.ToString(), values[i]);
			}
			return (int)(Decimal)comm.ExecuteScalar();
		
	}");
            codeOutput.WriteLine(@"	public DataTable GeneralQueryTable(string table, string []selectFields, params object []equalCriterias) {
				if (selectFields == null || selectFields.Length == 0) {
					throw new Exception(""You must supply at least one field for selection"");
				}
				if (equalCriterias != null && equalCriterias.Length > selectFields.Length) {
					throw new Exception(""Too many equal criterias"");
				}
				
					string query = ""SELECT "";
					bool first = true;
					foreach (string field in selectFields) {
						if (first) {
							query += field;
							first = false;
						} else {
							query += "", "" + field;
						}
					}

					query += "" FROM "" + table;

					if (equalCriterias != null && equalCriterias.Length > 0) {
						first = true;
						query += "" WHERE "";
						for (int i = 0; i < equalCriterias.Length; i++) {
							if (first) {
								query += selectFields[i] + "" LIKE @p"" + i.ToString();
								first = false;
							} else {
								query += "" AND "" + selectFields[i] + "" LIKE @p"" + i.ToString();
							}
						}
					}

                    SqlConnection dbConnection = new SqlConnection(""" + connectionString + @""");
                    dbConnection.Open();
					SqlCommand comm = new SqlCommand(query, dbConnection);
					if (equalCriterias != null && equalCriterias.Length > 0) {
						for (int i = 0; i < equalCriterias.Length; i++) {
							comm.Parameters.Add(""@p"" + i.ToString(), equalCriterias[i]);
						}
					}
					SqlDataAdapter da = new SqlDataAdapter(comm);
					DataTable dt = new DataTable();
					da.Fill(dt);
					return dt;
				
		}");
            generateMethodProxy(t);
            generateGetters(t);
            codeOutput.WriteLine("}");
        }

        private void generateGetters(Type t) {
            if (gettersGenerated.Contains(t.Name)) {
                return;
            } else {
                gettersGenerated.Add(t.Name);
            }

            ArrayList references = new ArrayList();
            Hashtable multipleRef = new Hashtable();

            codeOutput.WriteLine("    public " + t.Name + "[] get" + t.Name + "(string[] selectFields, params object[] equalCriterias) {");
            codeOutput.WriteLine("        DataTable dt = GeneralQueryTable(\"[" + prefix + "." + t.Name + "]\", selectFields, equalCriterias);");
            codeOutput.WriteLine("        " + t.Name + "[] objs = new " + t.Name + "[dt.Rows.Count];");
            codeOutput.WriteLine("        for (int i = 0; i < dt.Rows.Count; i++) {");
            codeOutput.WriteLine("            objs[i] = new " + t.Name + "();");
            codeOutput.WriteLine("            int id = (int) dt.Rows[i][\"ID\"];");
            foreach (FieldInfo f in t.GetFields()) {
                if (f.FieldType.IsClass && !f.FieldType.IsArray && !f.FieldType.Equals(typeof(string))) {
                    codeOutput.WriteLine("            int {0}ID = (int) dt.Rows[i][\"{0}_ID\"];", f.Name);
                    codeOutput.WriteLine("            " + f.FieldType.Name + "[] tmp" + f.Name + " = get" + f.FieldType.Name + "(new string[] {\"ID\", \"*\"}, " + f.Name + "ID);");
                    codeOutput.WriteLine("            if (tmp" + f.Name + ".Length > 0) {");
                    codeOutput.WriteLine("                objs[i]." + f.Name + " = tmp" + f.Name + "[0];");
                    codeOutput.WriteLine("            }");
                    references.Add(f);
                } else if (f.FieldType.IsArray) {
                    multipleRef[f.Name] = f;
                } else {
                    codeOutput.WriteLine("            objs[i]." + f.Name + " = (" + f.FieldType.Name + ") dt.Rows[i][\"" + f.Name + "\"];");
                }
            }
            if (multipleRef.Keys.Count > 0) {
                codeOutput.WriteLine("            DataTable dtRel;");
            }
            foreach (string field in multipleRef.Keys) {
                FieldInfo f = (FieldInfo)multipleRef[field];
                codeOutput.WriteLine("            dtRel = new DataTable();");
                codeOutput.WriteLine("            dtRel = GeneralQueryTable(\"[" + prefix + "." + t.Name + "_" + field + "]\", new string[] {\"" + t.Name + "_ID\", \"" + f.FieldType.GetElementType().Name + "_ID\"}, id);");
                codeOutput.WriteLine("            objs[i]." + f.Name + " = new " + f.FieldType.GetElementType().Name + "[dtRel.Rows.Count];");
                codeOutput.WriteLine("            for (int j = 0; j < dtRel.Rows.Count; j++) {");
                codeOutput.WriteLine("                int id2 = (int) dtRel.Rows[j][1];");
                codeOutput.WriteLine("                " + f.FieldType.GetElementType().Name + "[] tmp" + f.Name + " = get" + f.FieldType.GetElementType().Name + "(new string[] {\"ID\", \"*\"}, id2);");
                codeOutput.WriteLine("                if (tmp" + f.Name + ".Length > 0) {");
                codeOutput.WriteLine("                    objs[i]." + f.Name + "[j] = tmp" + f.Name + "[0];");
                codeOutput.WriteLine("                }");
                codeOutput.WriteLine("            }");
            }
            codeOutput.WriteLine("        }");
            codeOutput.WriteLine("        return objs;");
            codeOutput.WriteLine("    }");

            foreach (FieldInfo f in references) {
                generateGetters(f.FieldType);
            }
            foreach (FieldInfo f in multipleRef.Values) {
                generateGetters(f.FieldType.GetElementType());
            }
        }

        private void generateMethodProxy(Type t) {
            if (typesGenerated.Contains(t.Name)) {
                return;
            } else {
                typesGenerated.Add(t.Name);
            }

            ArrayList references = new ArrayList();
            Hashtable multipleRef = new Hashtable();

            codeOutput.WriteLine("    public int write" + t.Name + "(" + t.Name + " obj) {");
            codeOutput.WriteLine("        if (obj == null) {");
            codeOutput.WriteLine("            return 0;");
            codeOutput.WriteLine("        }");
            foreach (FieldInfo f in t.GetFields()) {
                if (f.FieldType.IsClass && !f.FieldType.IsArray && !f.FieldType.Equals(typeof(string))) {
                    codeOutput.WriteLine("        int {0}ID = write{1}(obj.{0});", f.Name, f.FieldType.Name);
                    references.Add(f);
                } else if (f.FieldType.IsArray) {
                    multipleRef[f.Name] = f;
                }
            }
            codeOutput.WriteLine("        int id = (int)(Decimal)GeneralInsertIdentity(\"[" + prefix + "." + t.Name + "]\", ");
            codeOutput.Write("            new string[] {");
            bool first = true;
            foreach (FieldInfo f in t.GetFields()) {
                if (!f.FieldType.IsArray) {
                    if (!first) {
                        codeOutput.Write(", ");
                    } else {
                        first = false;
                    }
                }
                if (f.FieldType.IsClass && !f.FieldType.IsArray && !f.FieldType.Equals(typeof(string))) {
                    codeOutput.Write("\"[" + f.Name + "_ID]\"");
                } else if (!f.FieldType.IsArray) {
                    codeOutput.Write("\"[" + f.Name + "]\"");
                }
            }
            codeOutput.WriteLine("}, ");
            first = true;
            foreach (FieldInfo f in t.GetFields()) {
                if (!f.FieldType.IsArray) {
                    if (!first) {
                        codeOutput.Write(", ");
                    } else {
                        codeOutput.Write("            ");
                        first = false;
                    }
                }
                if (f.FieldType.IsClass && !f.FieldType.IsArray && !f.FieldType.Equals(typeof(string))) {
                    codeOutput.Write(f.Name + "ID");
                } else if (!f.FieldType.IsArray) {
                    codeOutput.Write("obj." + f.Name);
                }
            }
            codeOutput.WriteLine(");");
            foreach (string field in multipleRef.Keys) {
                FieldInfo f = (FieldInfo)multipleRef[field];
                codeOutput.WriteLine("        foreach (" + f.FieldType.GetElementType().Name + " item in obj." + field + ") {");
                codeOutput.WriteLine("            int id2 = write" + f.FieldType.GetElementType().Name + "(item);");
                codeOutput.WriteLine("            GeneralInsert(\"[" + prefix + "." + t.Name + "_" + field + "]\", ");
                codeOutput.WriteLine("                new string[] {\"" + t.Name + "_ID\", \"" + f.FieldType.GetElementType().Name + "_ID\"}, ");
                codeOutput.WriteLine("                id, id2);");
                codeOutput.WriteLine("        }");
            }
            codeOutput.WriteLine("        return id;");
            codeOutput.WriteLine("    }");

            foreach (FieldInfo f in references) {
                generateMethodProxy(f.FieldType);
            }
            foreach (FieldInfo f in multipleRef.Values) {
                generateMethodProxy(f.FieldType.GetElementType());
            }
        }
    }
}
